Skip to content

gh-89977: Avoid releasing the GIL in nonblocking socket operations - #29579

Open
jcrist wants to merge 4 commits into
python:mainfrom
jcrist:keep-gil-for-fast-syscalls
Open

jcrist wants to merge 4 commits into
python:mainfrom
jcrist:keep-gil-for-fast-syscalls

Conversation

@jcrist

@jcrist jcrist commented Nov 16, 2021

Copy link
Copy Markdown
Contributor

Previously, every operation in socket would release the GIL, allowing
other threads to progress. This makes sense when using threads and
blocking sockets, but when using non-blocking sockets (as done with
asyncio) these operations return quickly. In the presence of background
threads this can lead to the "convoy effect", where the GIL is acquired
and held by the background thread every time the IO thread releases it,
and then the IO thread blocks until it regains ownership of the GIL,
causing a massive decrease in IO operations per second.

One possible fix for this is to have IO-heavy threads release the GIL
less frequently. In the case of asyncio, the following changes were
needed:

  • Don't release the GIL for socket operations on non-blocking sockets.
  • Don't release the GIL for select (epoll, select, kqueue, ...) calls
    with a timeout of 0.

See the corresponding issue at https://bugs.python.org/issue45819
for more information.

https://bugs.python.org/issue45819

Previously, every operation in `socket` would release the GIL, allowing
other threads to progress. This makes sense when using threads and
blocking sockets, but when using non-blocking sockets (as done with
asyncio) these operations return quickly. In the presence of background
threads this can lead to the "convoy effect", where the GIL is acquired
and held by the background thread every time the IO thread releases it,
and then the IO thread blocks until it regains ownership of the GIL,
causing a massive decrease in IO operations per second.

One possible fix for this is to have IO-heavy threads release the GIL
less frequently. In the case of asyncio, the following changes were
needed:

- Don't release the GIL for socket operations on non-blocking sockets.
- Don't release the GIL for select (epoll, select, kqueue, ...) calls
with a timeout of 0.

With these changes, asyncio doesn't see as degraded of an output when a
background thread holds the GIL.
Comment thread Include/ceval.h


/* Conditionally release and restore the GIL. */
#define _Py_BEGIN_ALLOW_THREADS_COND(cond) \

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some helper macros here, but am not sure if that's best practice. Happy to change this if others have a better suggestion.

Comment thread Modules/selectmodule.c
ev.data.fd = fd;
Py_BEGIN_ALLOW_THREADS
result = epoll_ctl(epfd, op, fd, &ev);
Py_END_ALLOW_THREADS

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell epoll_ctl operations like this always complete quickly, so releasing the GIL here isn't really necessary.

Comment thread Modules/socketmodule.c
_Py_BEGIN_ALLOW_THREADS_COND(s->sock_timeout)
res = sock_func(s, data);
Py_END_ALLOW_THREADS
_Py_END_ALLOW_THREADS_COND

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are likely other socket operations where releasing the GIL could be avoided, but this gets the bulk of those done frequently in a loop (send/ recv, ...). socket.close() is also handled below.

@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions Bot added the stale Stale PR or inactive for long period of time. label Dec 17, 2021
Comment thread Include/ceval.h Outdated
Comment thread Modules/socketmodule.c
Comment on lines -3150 to +3152
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_ALLOW_THREADS_COND(s->sock_timeout)
res = SOCKETCLOSE(fd);
Py_END_ALLOW_THREADS
_Py_END_ALLOW_THREADS_COND

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't change here. s->sock_timeout doesn't affect to close.
Unlike send/recv, close can not be called frequently more than creating socket. So this shouldn't be a big performance problem.

@bedevere-bot

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@github-actions github-actions Bot removed the stale Stale PR or inactive for long period of time. label Apr 10, 2026
@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions Bot added the stale Stale PR or inactive for long period of time. label May 11, 2026
@github-actions github-actions Bot removed the stale Stale PR or inactive for long period of time. label Aug 12, 2026
@serhiy-storchaka serhiy-storchaka changed the title bpo-45819: Avoid releasing the GIL in nonblocking socket operations gh-89977: Avoid releasing the GIL in nonblocking socket operations Aug 12, 2026
@methane

methane commented Sep 7, 2026

Copy link
Copy Markdown
Member

@codex review this. Please check whether it is really safe to stop releasing the GIL, including whether there are any cases on officially supported Python platforms where this operation can block for a significant amount of time.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-09-07T04:30:18.109889Z 196f9e6 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

An unknown error occurred
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@methane

methane commented Sep 7, 2026

Copy link
Copy Markdown
Member

@copilot resolve the merge conflicts in this pull request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants